package net.guerlab.smart.platform.user.web.controller;

import io.swagger.annotations.ApiOperation;
import net.guerlab.commons.number.NumberHelper;
import net.guerlab.smart.platform.basic.auth.annotation.IgnoreLogin;
import net.guerlab.smart.platform.commons.exception.PasswordErrorException;
import net.guerlab.smart.platform.commons.exception.ThirdPartyIdInvalidException;
import net.guerlab.smart.platform.commons.exception.UserNotFindException;
import net.guerlab.smart.platform.server.utils.IpUtils;
import net.guerlab.smart.platform.user.auth.UserContextHandler;
import net.guerlab.smart.platform.user.auth.utils.UserJwtHelper;
import net.guerlab.smart.platform.user.core.domain.UserDTO;
import net.guerlab.smart.platform.user.core.exception.UserHasBoundException;
import net.guerlab.smart.platform.user.core.searchparams.UserOauthSearchParams;
import net.guerlab.smart.platform.user.core.searchparams.UserSearchParams;
import net.guerlab.smart.platform.user.service.entity.User;
import net.guerlab.smart.platform.user.service.entity.UserOauth;
import net.guerlab.smart.platform.user.service.service.LoginLogService;
import net.guerlab.smart.platform.user.service.service.UserOauthService;
import net.guerlab.smart.platform.user.service.service.UserService;
import net.guerlab.smart.platform.user.web.domain.BindRequest;
import net.guerlab.smart.platform.user.web.domain.LoginResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

/**
 * 钉钉-控制面板
 *
 * @author guer
 */
@Transactional(rollbackFor = Exception.class)
public abstract class AbstractOauthControlPanelController {

    private UserService userService;

    private UserOauthService userOauthService;

    private LoginLogService loginLogService;

    private UserJwtHelper jwtHelper;

    /**
     * 获取oauth类型
     *
     * @return oauth类型
     */
    protected abstract String getOauthType();

    @IgnoreLogin
    @ApiOperation("绑定")
    @PostMapping("/bind")
    public LoginResponse bind(@RequestBody BindRequest bindRequest, HttpServletRequest request) {
        String thirdPartyId = StringUtils.trimToNull(bindRequest.getThirdPartyId());
        if (thirdPartyId == null) {
            throw new ThirdPartyIdInvalidException();
        }
        if (findUser(thirdPartyId) != null) {
            throw new UserHasBoundException();
        }

        User user = userService.selectByUsername(bindRequest.getUsername());

        if (user == null) {
            throw new UserNotFindException();
        }

        if (getBind(user.getUserId()) != null) {
            throw new UserHasBoundException();
        }

        if (userService.checkPasswordError(user, bindRequest.getPassword())) {
            throw new PasswordErrorException();
        }

        UserOauth userOauth = new UserOauth();
        userOauth.setUserId(user.getUserId());
        userOauth.setType(getOauthType());
        userOauth.setThirdPartyId(thirdPartyId);

        userOauthService.insert(userOauth);

        return getLoginSucceedDTO(user, thirdPartyId, request, getOauthType());
    }

    @ApiOperation("解绑")
    @PostMapping("/unbind")
    public void bind() {
        Long userId = UserContextHandler.getUserId();

        UserOauth userOauth = new UserOauth();
        userOauth.setUserId(userId);
        userOauth.setType(getOauthType());

        userOauthService.delete(userOauth);
    }

    @ApiOperation("获取绑定状态")
    @GetMapping("/bindStatus")
    public boolean bindStatus() {
        return getBind(UserContextHandler.getUserId()) != null;
    }

    protected LoginResponse getLoginSucceedDTO(User user, String openId, HttpServletRequest request, String loginType) {
        LoginResponse loginResponse = user == null ? new LoginResponse() : getLoginSucceedDTO(user);

        UserOauth userOauth = new UserOauth();
        userOauth.setUserId(user == null ? null : user.getUserId());
        userOauth.setType(getOauthType());
        userOauth.setThirdPartyId(openId);

        loginResponse.setThirdParty(userOauth.toDTO());

        if (user == null) {
            return loginResponse;
        }

        UserSearchParams searchParams = new UserSearchParams();
        searchParams.setUserId(user.getUserId());

        User updateInfo = new User();
        updateInfo.setLastLoginTime(LocalDateTime.now());

        userService.updateByExampleSelective(updateInfo, userService.getExample(searchParams));

        loginLogService.addSuccessLog(user.getUserId(), user.getName(), openId, loginType, IpUtils.getIp(request));

        return loginResponse;
    }

    protected UserOauth getBind(Long userId) {
        return userOauthService.getOne(getOauthType(), userId);
    }

    protected User findUser(String openId) {
        UserOauthSearchParams oauthSearchParams = new UserOauthSearchParams();
        oauthSearchParams.setType(getOauthType());
        oauthSearchParams.setThirdPartyId(openId);

        UserOauth userOauth = userOauthService.selectOne(oauthSearchParams);

        if (userOauth == null || !NumberHelper.greaterZero(userOauth.getUserId())) {
            return null;
        }

        return userService.selectById(userOauth.getUserId());
    }

    protected LoginResponse getLoginSucceedDTO(User user) {
        UserDTO dto = user.toDTO();

        LoginResponse loginSucceedDTO = new LoginResponse();
        loginSucceedDTO.setInfo(dto);
        loginSucceedDTO.setAccessToken(jwtHelper.generateByAccessToken(user));
        loginSucceedDTO.setRefreshToken(jwtHelper.generateByRefreshToken(user));

        return loginSucceedDTO;
    }

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Autowired
    public void setUserOauthService(UserOauthService userOauthService) {
        this.userOauthService = userOauthService;
    }

    @Autowired
    public void setLoginLogService(LoginLogService loginLogService) {
        this.loginLogService = loginLogService;
    }

    @Autowired
    public void setJwtHelper(UserJwtHelper jwtHelper) {
        this.jwtHelper = jwtHelper;
    }
}
